#include <stdio.h>
#include "c-utils/internal/base64.h"

unsigned char b64_chr[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

unsigned int b64_int(unsigned int ch)
{

	/* ASCII to base64_int
	 65-90  Upper Case  >>  0-25
	 97-122 Lower Case  >>  26-51
	 48-57  Numbers     >>  52-61
	 43     Plus (+)    >>  62
	 47     Slash (/)   >>  63
	 61     Equal (=)   >>  64~
	*/
	if (ch == 43)
		return 62;
	if (ch == 47)
		return 63;
	if (ch == 61)
		return 64;
	if ((ch > 47) && (ch < 58))
		return ch + 4;
	if ((ch > 64) && (ch < 91))
		return ch - 'A';
	if ((ch > 96) && (ch < 123))
		return (ch - 'a') + 26;
	return 0;
}

unsigned int utils_b64_encode(const unsigned char *in, unsigned int in_len, unsigned char *out)
{

	unsigned int i = 0, j = 0, k = 0, s[3];

	for (i = 0; i < in_len; i++)
	{
		s[j++] = *(in + i);
		if (j == 3)
		{
			out[k + 0] = b64_chr[(s[0] & 255) >> 2];
			out[k + 1] = b64_chr[((s[0] & 0x03) << 4) + ((s[1] & 0xF0) >> 4)];
			out[k + 2] = b64_chr[((s[1] & 0x0F) << 2) + ((s[2] & 0xC0) >> 6)];
			out[k + 3] = b64_chr[s[2] & 0x3F];
			j = 0;
			k += 4;
		}
	}

	if (j)
	{
		if (j == 1)
			s[1] = 0;
		out[k + 0] = b64_chr[(s[0] & 255) >> 2];
		out[k + 1] = b64_chr[((s[0] & 0x03) << 4) + ((s[1] & 0xF0) >> 4)];
		if (j == 2)
			out[k + 2] = b64_chr[((s[1] & 0x0F) << 2)];
		else
			out[k + 2] = '=';
		out[k + 3] = '=';
		k += 4;
	}

	out[k] = '\0';

	return k;
}

unsigned int utils_b64_decode(const unsigned char *in, unsigned int in_len, unsigned char *out)
{

	unsigned int i = 0, j = 0, k = 0, s[4];

	for (i = 0; i < in_len; i++)
	{
		s[j++] = b64_int(*(in + i));
		if (j == 4)
		{
			out[k + 0] = ((s[0] & 255) << 2) + ((s[1] & 0x30) >> 4);
			if (s[2] != 64)
			{
				out[k + 1] = ((s[1] & 0x0F) << 4) + ((s[2] & 0x3C) >> 2);
				if ((s[3] != 64))
				{
					out[k + 2] = ((s[2] & 0x03) << 6) + (s[3]);
					k += 3;
				}
				else
				{
					k += 2;
				}
			}
			else
			{
				k += 1;
			}
			j = 0;
		}
	}

	return k;
}

unsigned int utils_b64_encode_file(const char *in_file_path, char *out_file_path)
{

	FILE *fp_in = fopen(in_file_path, "rb");
	FILE *fp_out = fopen(out_file_path, "wb");

	unsigned int i = 0;
	unsigned int j = 0;
	unsigned int c = 0;
	unsigned int s[4];

	if ((fp_in == NULL) || (fp_out == NULL))
	{
		if (fp_in != NULL)
		{
			fclose(fp_in);
		}
		if (fp_out != NULL)
		{
			fclose(fp_out);
		}
		return 0;
	}

	while (c != EOF)
	{
		c = fgetc(fp_in);
		if (c == EOF)
			break;
		s[j++] = c;
		if (j == 3)
		{
			fputc(b64_chr[(s[0] & 255) >> 2], fp_out);
			fputc(b64_chr[((s[0] & 0x03) << 4) + ((s[1] & 0xF0) >> 4)], fp_out);
			fputc(b64_chr[((s[1] & 0x0F) << 2) + ((s[2] & 0xC0) >> 6)], fp_out);
			fputc(b64_chr[s[2] & 0x3F], fp_out);
			j = 0;
			i += 4;
		}
	}

	if (j)
	{
		if (j == 1)
			s[1] = 0;
		fputc(b64_chr[(s[0] & 255) >> 2], fp_out);
		fputc(b64_chr[((s[0] & 0x03) << 4) + ((s[1] & 0xF0) >> 4)], fp_out);
		if (j == 2)
			fputc(b64_chr[((s[1] & 0x0F) << 2)], fp_out);
		else
			fputc('=', fp_out);
		fputc('=', fp_out);
		i += 4;
	}

	fclose(fp_in);
	fclose(fp_out);

	return i;
}

unsigned int uitls_b64_decode_file(const char *in_file_path, char *out_file_path)
{

	FILE *fp_in = fopen(in_file_path, "rb");
	FILE *fp_out = fopen(out_file_path, "wb");

	unsigned int c = 0;
	unsigned int j = 0;
	unsigned int k = 0;
	unsigned int s[4];

	if ((fp_in == NULL) || (fp_out == NULL))
	{
		if (fp_in != NULL)
		{
			fclose(fp_in);
		}
		if (fp_out != NULL)
		{
			fclose(fp_out);
		}
		return 0;
	}

	while (c != EOF)
	{
		c = fgetc(fp_in);
		if (c == EOF)
			break;
		s[j++] = b64_int(c);
		if (j == 4)
		{
			fputc(((s[0] & 255) << 2) + ((s[1] & 0x30) >> 4), fp_out);
			if (s[2] != 64)
			{
				fputc(((s[1] & 0x0F) << 4) + ((s[2] & 0x3C) >> 2), fp_out);
				if ((s[3] != 64))
				{
					fputc(((s[2] & 0x03) << 6) + (s[3]), fp_out);
					k += 3;
				}
				else
				{
					k += 2;
				}
			}
			else
			{
				k += 1;
			}
			j = 0;
		}
	}

	fclose(fp_in);
	fclose(fp_out);

	return k;
}